# composer.rb
# Copyright (C) 2004-2009 Akira TAGOH

# Authors:
#   Akira TAGOH  <akira@tagoh.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

require 'prune/error'
require 'prune/debug'
require 'prune/message'


module PRUNE

=begin rdoc

== PRUNE::MessageComposer

=end

  class MessageComposer
    include PRUNE::Debug

    TYPE_STRING = 0
    TYPE_OBJECT = 1

=begin rdoc

=== PRUNE::MessageComposer#new(msgformat = nil)

=end

    def initialize(msgformat = nil)
      @params = []
      @msgformat = nil

      self.format = msgformat unless msgformat.nil?
    end # def initialize

=begin rdoc

=== PRUNE::MessageComposer#format=(val)

=end

    def format=(val)
      p = val
      @params.clear
      until p.empty? do
        p = p.sub(/(__[A-Z0-9_]+__)/, '')
        if $1.nil? then
          p = ""
        else
          @params.push($1)
        end
      end
      p = val
      @params[0..-2].each do |m|
        p = p.sub(/#{m}/, '(\S+)')
      end
      p = p.sub(/#{@params[-1]}/, '(.*)')
      @msgformat = p
    end # def format=

=begin rdoc

=== PRUNE::MessageComposer#each_symbol(&block)

=end

    def each_symbol(&block) # :yields: symbol
      @params.each(&block)
    end # def each_symbol

=begin rdoc

=== PRUNE::MessageComposer#parse(message)

=end

    def parse(message)
      PRUNE.Fail(PRUNE::Error::InvalidMessageType, message.class) unless message.kind_of?(PRUNE::Message::Core)
      return nil if @msgformat.nil?

      retval = nil
      fmt = @msgformat.dup

      @params.dup.each do |p|
        if message.params(-1) =~ /#{fmt}/ then
          val = $1
          retval = {} if retval.nil?
          retval[p] = val
          fmt = fmt.sub(/\(\\S\+\)|\(\.\*\)/, val)
        else
          retval = nil
          break
        end
      end
      if block_given? && !retval.nil? then
        retval.each do |key, value|
          yield key, value
        end
      end

      retval
    end # def parse

=begin rdoc

=== PRUNE::MessageComposer#compose(table)

=end

    def compose(table)
      retval = @msgformat.dup
      skip = false

      @params.dup.each do |p|
        if skip then
          skip = false
          next
        end
        unless table.has_key?(p) then
          warning("Unknown compose tag `%s' is found.", p)
        else
          if table[p].kind_of?(PRUNE::TYPE::ComposeInfoStruct) then
            case table[p].composetype
            when PRUNE::MessageComposer::TYPE_STRING
              val = table[p].instance.nil? ? "" : table[p].instance
              retval = retval.sub(/\(\\S\+\)|\(\.\*\)/, val)
            when PRUNE::MessageComposer::TYPE_OBJECT
              retval = table[p].instance.send(table[p].method, retval, /\(\\S\+\)|\(\.\*\)/, *table[p].args)
            else
              warning("Unknown compose type `%s' is given.", table[p].composetype)
              return nil
            end
          else
            PRUNE.Fail(PRUNE::Error::InvalidComposeTable, table[p].class)
            return nil
          end
        end
        if retval =~ (/\(\|/) then
          retval = retval.sub(/\A(.*)\(\|(\((?:\\S\+|\.\*)\))\)(.*)\Z/, '\1\2\3')
        elsif retval =~ (/\((?!\((?:\.\*|\\S\+)\))\S+\|\((?:\.\*|\\S\+)\)\)/) then
          retval = retval.sub(/\A(.*)\(((?!\((?:\.\*|\\S\+)\))\S+)\|\((?:\.\*|\\S\+)\)\)(.*)\Z/, '\1\2\3')
          skip = true
        end
      end
      retval.gsub!(/\\([()])/, '\1')

      retval
    end # def compose

  end # class MessageComposer

end # module PRUNE
